<?php

namespace App\Http\Controllers\Admin;

use App\Exports\CommodityActivityExport;
use App\Exports\InvoiceExport;
use App\Exports\StockingExport;
use App\Http\Controllers\Controller;
use App\Models\Commodity;
use App\Models\CommodityActivity;
use App\Models\CommodityActivityPushMaterial;
use App\Models\CommodityActivitySku;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Database\Eloquent\Relations\MorphToMany;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;

class CommodityActivityController extends Controller
{
    public function store(Request $request)
    {
        $this->validateSkuReq($request);

        $activity = null;
        try {
            DB::beginTransaction();

            if ($copiedFrom = $request->get('copied_from')) {
                // 如果是复制, 获取被复制实例, 根据被复制实例创建
                $sourceInstance = CommodityActivity::query()
                    ->with([
                        'attributes' => fn($builder) => $builder->with(['values.attachments']),
                        'materials.attachments',
                        'skus.attachments'
                    ])
                    ->findOrFail($copiedFrom);
            } else {
                // 新增的情况, 根据商品库商品创建
                $sourceInstance = Commodity::query()
                    ->with([
                        'attributes' => fn($builder) => $builder->with(['values.attachments']), // 加载 规格值的组图
                        'skus' => fn($builder) => $builder->with(['attachments']) // 加载 sku 的组图
                    ])
                    ->findOrFail($request->get('commodity_id'));
            }

            // 活动主体
            $activity = CommodityActivity::query()->create($request->only([
                'name', 'commodity_id', 'type', 'panic_buying_start_time', 'panic_buying_end_time', 'sort_id',
                'live_broadcast_url', 'live_broadcast_information', 'desc', 'free_shipping_minimal', 'free_shipping_maximum',
                'freight', 'after_sale_deadline', 'estimated_delivery_time', 'status', 'locking', 'address_purchase_restriction',
                'remote_regions_freight', 'remote_regions_freight_setting'
            ]));
            $activity->update(['numbering' => CommodityActivity::TYPE_NUMBERING_PREFIX_MAP[$request->type] . str_pad($activity['id'], 5, 0, STR_PAD_LEFT)]); // 编号
            // 活动关联标签
            $activity->labels()->sync((array) $request->get('activity_label'));

            // 活动的图片
            $addPivotType = function ($attachmentIdArr, $type) {
                $temp = [];
                foreach ($attachmentIdArr as $attachmentId) {
                    $temp[$attachmentId] = ['type' => $type];
                }
                return $temp;
            };
            $activity->attachments()->sync(
            // 主图
                $addPivotType(
                    $request->get('main_attachment_ids'),
                    CommodityActivity::ATTACHMENT_TYPE_MAIN_PIC
                )
                +
                // 详情图
                $addPivotType(
                    $request->get('detail_attachment_ids'),
                    CommodityActivity::ATTACHMENT_TYPE_DETAIL_PIC
                )
            );

            // 活动 SKU
            // 取商品 SKU
            $attrWithValue = [];
            foreach ((empty($copiedFrom) ? $sourceInstance->attributes : $sourceInstance->attributes->reverse()) as $attribute) { // 复制活动时, 需要颠倒下数组顺序
                $attr = $activity->attributes()->create(['name' => $attribute['name']]); // attribute key

                $attributeValue = $attribute['values']->toArray();
                // sku 规格 value 是否有附件
                if (!empty(array_filter(
                    array_column($attributeValue, 'attachments'), // 获取 value 的附件信息
                    fn($item) => !empty($item) // 过滤为空的数据
                ))) {
                    $attrValues = new Collection();
                    // 单独处理 sku 每个 value 并加上 商品 sku 组图
                    foreach ($attributeValue as $item) {
                        $attributeValuesTemp = $attr->values()->create(['content' => $item['content']]);
                        isset($item['attachments'][0]) && $attributeValuesTemp->attachments()->sync($item['attachments'][0]['id']);
                        $attrValues->add($attributeValuesTemp);
                    }
                } else {
                    $attrValues = $attr->values()->createMany( // values
                        array_map(
                            function ($value) {
                                return ['content' => $value['content']];
                            },
                            $attribute['values']->toArray()
                        )
                    );
                }

                // 组装活动 SKU 属性对应 商品 SKU 属性的数组
                $attrWithValue[$attribute['id']] = ['new_id' => $attr['id']];
                foreach ($attrValues as $value) {
                    $attrWithValue[$attribute['id']]['values'][$value['content']] = $value['id'];
                }
            }

            // 写入商品活动 SKU 数据
            $commoditySkus = arrayCombineByKey($sourceInstance['skus']->toArray(), 'id');
            foreach ($request->get('sku', []) as $sku) {
                $activitySku = $activity->skus()->create(array_merge(
                    Arr::only($sku, [
                        'strongman_price', 'elite_price', 'group_owner_price', 'external_guide_price', 'direct_invitation',
                        'indirect_invitation', 'stock',
//                        'estimated_sales' // 已废弃
                    ]),
                    // 当是复制时, 因为之前的活动, 基于的商品 SKU, 可能已经被改变了, 导致商品 sku id 对不上, 以及 sku 对不上, 所以这里按照前端提交的为准
                    Arr::only($commoditySkus[$sku['sku_id']], [ // 这里的 id 被前端的组件占用了 id 字段换成 sku_id 来传递 id
                        'entry_price', 'purchase_price'
                    ]),
                    [
                        'commodity_id' => $sourceInstance['id'],
                        'original_stock' => $sku['stock']
                    ]
                ));
                // 商品活动 sku 对应的 value
                $activitySku->values()->sync(array_map(
                    fn($attribute) => $attrWithValue[$attribute['id']]['values'][$sku[$attribute['id']]],
                    $sourceInstance['attributes']->toArray()
                ));
                // 商品 sku 组图 => 商品活动 sku 组图
                if (!empty($commoditySkus[$sku['sku_id']]['attachments'])) {
                    $activitySku->attachments()->sync($commoditySkus[$sku['sku_id']]['attachments'][0]['id']);
                }
            }

            // 复制的情况, copy 被复制实例转发素材
            $isCopy = $sourceInstance !== null && !empty($request->get('copied_from'));
            if ($isCopy) {
                // 这里得翻转下,不然顺序会颠倒
                foreach ($sourceInstance->materials->reverse() as $material) {
                    $model = $activity->materials()->create([
                        'type' => $material['type'],
                        'content' => (int) $material['type'] !== CommodityActivityPushMaterial::TYPE_PICTURE ? $material['content'] : ''
                    ]);
                    if ($material['type'] === CommodityActivityPushMaterial::TYPE_PICTURE) {
                        $model->attachments()->sync(array_column($material->attachments->toArray(), 'id'));
                    }
                }
            }

            activity()
                ->on($isCopy ? $sourceInstance : $activity)
                ->by(\Auth::user())
                ->withProperty('ip', app('request')->ip())
                ->log(('创建商品活动: ' . ($activity['name'] ?? '') . ' (' . ($activity['numbering'] ?? '') . ')') . ($isCopy ? sprintf('(复制于商品活动: %s [%s])', $sourceInstance['name'] ?? '', $sourceInstance['numbering'] ?? '') : ''));

            DB::commit();
        } catch (\Exception $e) {
            DB::rollBack();
            \Log::error($e);
            $this->response->errorBadRequest($e->getMessage());
        }

        \App\Services\Commodity::forgetCommodityActivityListCacheTag(); // 清除缓存
        return $this->response->created(null, $activity);
    }

    public function update(Request $request, $id)
    {
        $this->validateSkuReq($request);

        try {
            DB::beginTransaction();

            $commodityActivity = CommodityActivity::query()->with(['skus.attachments'])->findOrFail($id);
            $commodityActivity->update($request->only([
                'name', 'panic_buying_start_time', 'panic_buying_end_time', 'sort_id', 'live_broadcast_url', 'live_broadcast_information',
                'desc', 'freight', 'free_shipping_minimal', 'free_shipping_maximum', 'after_sale_deadline', 'estimated_delivery_time',
                'status', 'locking', 'address_purchase_restriction', 'remote_regions_freight', 'remote_regions_freight_setting'
            ]));

            // 活动关联标签
            $commodityActivity->labels()->sync((array) $request->get('activity_label'));
            // 活动的图片
            $addPivotType = function ($attachmentIdArr, $type) {
                $temp = [];
                foreach ($attachmentIdArr as $attachmentId) {
                    $temp[$attachmentId] = ['type' => $type];
                }
                return $temp;
            };
            $commodityActivity->attachments()
                ->sync(
                // 主图
                    $addPivotType(
                        $request->get('main_attachment_ids'),
                        CommodityActivity::ATTACHMENT_TYPE_MAIN_PIC
                    )
                    +
                    // 详情图
                    $addPivotType(
                        $request->get('detail_attachment_ids'),
                        CommodityActivity::ATTACHMENT_TYPE_DETAIL_PIC
                    )
                );

            // 更新 SKU
            $skusReq = arrayCombineByKey($request->get('sku', []), 'sku_id');
            foreach ($commodityActivity['skus'] as $sku) {
                $reqSku = $skusReq[$sku['id']];
                if (empty($reqSku)) {
                    continue;
                }
                $sku->update(Arr::only($reqSku, [
                    'strongman_price', 'elite_price', 'group_owner_price',
                    'external_guide_price', 'direct_invitation', 'indirect_invitation', 'stock',
//                    'estimated_sales', // 已废弃
                ]));
                // 商品 sku 组图 => 商品活动 sku 组图
                // 暂不支持更换 SKU 组图
//                if (!empty($reqSku)) {
//                    $sku->attachments()->sync($reqSku['sku_picture'][0]['id']);
//                }
            }

            DB::commit();
        } catch (\Exception $e) {
            DB::rollBack();
            \Log::error($e);
            $this->response->errorBadRequest('更新发生错误: ' . $e->getMessage());
        }

        \App\Services\Commodity::forgetCommodityActivityListCacheTag(); // 清除缓存
        \App\Services\Commodity::forgetCommodityActivityInfoCache($id);

        activity()
            ->on($commodityActivity)
            ->by(\Auth::user())
            ->withProperty('ip', app('request')->ip())
            ->log(('编辑商品活动: ' . ($commodityActivity['name'] ?? '') . ' (' . ($commodityActivity['numbering'] ?? '') . ')'));

        return $this->response->noContent();
    }

    private function validateSkuReq(Request $request)
    {
        $skuReq = $request->get('sku');
        if (empty($skuReq)) {
            $this->response->errorBadRequest('SKU 数据不能为空');
        }
        foreach ($skuReq as $sku) {
            validator($sku, [
                'strongman_price' => 'integer|required|min:0',
                'elite_price' => 'integer|required|gte:strongman_price|min:0',
                'group_owner_price' => 'integer|required|gte:elite_price|min:0',
                'external_guide_price' => 'integer|required|min:0',
                'direct_invitation' => 'integer|required|min:0',
                'indirect_invitation' => 'integer|required|min:0',
                'stock' => 'integer|required|min:0',
//                'estimated_sales' => 'integer|required|min:0', // 已废弃
                'address_purchase_restriction' => 'integer|min:0',
            ], [
                'strongman_price.integer' => '大咖金额不合法',
                'strongman_price.required' => '大咖金额为必填项',
                'strongman_price.min' => '大咖金额不得小于零元',
                'elite_price.integer' => '精英金额不合法',
                'elite_price.required' => '精英金额为必填项',
                'elite_price.min' => '精英金额不得小于零元',
                'elite_price.gte' => '精英金额不得小于大咖金额',
                'group_owner_price.integer' => '群主金额不合法',
                'group_owner_price.required' => '群主金额为必填项',
                'group_owner_price.min' => '群主金额不得小于零元',
                'group_owner_price.gte' => '群主金额不得小于精英金额',
                'external_guide_price.integer' => '对外指导价金额不合法',
                'external_guide_price.required' => '对外指导价金额为必填项',
                'external_guide_price.min' => '对外指导价金额不得小于零元',
                'direct_invitation.integer' => '直接邀请金额不合法',
                'direct_invitation.required' => '直接邀请金额为必填项',
                'direct_invitation.min' => '直接邀请金额不得小于零元',
                'stock.integer' => '数量不合法',
                'stock.required' => '数量为必填项',
                'stock.min' => '数量不得小于零',
//                'estimated_sales.integer' => '预计销量不合法', // 已废弃
//                'estimated_sales.required' => '预计销量为必填项',
//                'estimated_sales.min' => '预计销量不得小于零',
                'address_purchase_restriction.integer' => '限购数量不合法',
                'address_purchase_restriction.min' => '预计销量不得小于零',
            ])->validate();
        }
    }

    public function list(Request $request)
    {
        if ($request->has('export')) {
            return (new CommodityActivityExport($request))->download(
                sprintf(((int) $request->get('type') === CommodityActivity::TYPE_LIVE_TODAY ? '今日直播活动' : '好评返团活动') . '(%s).xls', Carbon::now()->format('YmdHisu')));
        }

        return $this->response->array(
            CommodityActivity::query()
                ->with([
                    'labels.attachments',
                    'commodity' => function ($builder) {
                        $builder
                            ->withTrashed()
                            ->with(['category', 'supplier']);
                    },
                    'attachments' => function (MorphToMany $morphToMany) {
                        $morphToMany
                            ->wherePivot('type', CommodityActivity::ATTACHMENT_TYPE_MAIN_PIC);
                    },
                    'attributes.values',
                    'skus.values',
                ])
                ->when($request->get('label'), function (Builder $builder, $label) {
                    $builder->whereHas('labels', function (Builder $builder) use ($label) {
                        $builder->where('label_id', $label);
                    });
                })
                ->when(
                    $request->get('name') || $request->get('category') || $request->get('numbering'),
                    function (Builder $builder) use ($request) {
                        $builder
                            ->join('commodities', 'commodities.id', '=', 'commodity_activities.commodity_id')
                            ->when($request->get('name'), function (Builder $builder, $name) {
                                $builder->where('commodities.name', 'like', "%$name%");
                            })
                            ->when($request->get('category'), function (Builder $builder, $category) {
                                $builder->where('commodities.category_id', $category);
                            })
                            ->when($request->get('numbering'), function (Builder $builder, $numbering) {
                                $builder->where('commodities.numbering', 'like', "%$numbering%");
                            });
                    })
                ->when($request->get('activity_numbering'), function (Builder $builder, $activityNumbering) {
                    $builder->where('commodity_activities.numbering', 'like', "%$activityNumbering%");
                })
                ->when($request->get('type'), function (Builder $builder, $type) {
                    $builder->where('commodity_activities.type', $type);
                })
                ->when($request->get('locking'), function (Builder $builder, $locking) {
                    $builder->where('commodity_activities.locking', $locking);
                })
                ->when($request->get('updated_at_begin'), function (Builder $query, $createdAtBegin) {
                    $query->where('commodity_activities.updated_at', '>=', $createdAtBegin);
                })
                ->when($request->get('updated_at_end'), function (Builder $query, $createdAtEnd) {
                    $query->where('commodity_activities.updated_at', '<=', Carbon::parse($createdAtEnd)->endOfDay());
                })
                ->when($request->get('panic_buying_start_time') && $request->get('panic_buying_end_time'), function (Builder $builder) use ($request) {
                    $betweenArr = [
                        Carbon::parse($request->get('panic_buying_start_time'))->startOfDay()->format('Y-m-d H:i:s'),
                        Carbon::parse($request->get('panic_buying_end_time'))->endOfDay()->format('Y-m-d H:i:s')
                    ];
                    $builder->whereBetween('commodity_activities.panic_buying_start_time', $betweenArr);
                })
                ->when($request->get('status'), function (Builder $query, $status) {
                    if ((int) $status === CommodityActivity::STATUS_OVER) {
                        $query->where('panic_buying_end_time', '<=', Carbon::now());
                    } else {
                        $query->where('commodity_activities.status', $status);
                    }
                })
                ->select(['commodity_activities.*'])
                ->paginate()
        );
    }

    public function delete($id)
    {
        try {
            $activity = CommodityActivity::query()->findOrFail($id);

            if ($activity->status === CommodityActivity::STATUS_PUT_ON_SHELF) {
                $this->response->errorBadRequest('上架中的活动不能进行删除');
            } else {
                $activity->delete();
            }
        } catch (\Exception $e) {

        }

        \App\Services\Commodity::forgetCommodityActivityListCacheTag(); // 清除缓存
        \App\Services\Commodity::forgetCommodityActivityInfoCache($id);
        return $this->response->noContent();
    }

    public function updateStatus(Request $request, $id)
    {
        try {
            $activity = CommodityActivity::query()->findOrFail($id);
            $activity->update(['status' => $request->get('status')]);
        } catch (\Exception $e) {
            $this->response->errorBadRequest($e->getMessage());
        }

        \App\Services\Commodity::forgetCommodityActivityListCacheTag(); // 清除缓存
        \App\Services\Commodity::forgetCommodityActivityInfoCache($id);
        return $this->response->created(null);
    }

    public function updateLocking(Request $request, $id)
    {
        try {
            if (!is_numeric($id)) {
                $activity = CommodityActivity::query()->where('numbering', $id)->firstOrFail();
            } else {
                $activity = CommodityActivity::query()->findOrFail($id);
            }
            if (!array_key_exists((int) $request->get('locking'), CommodityActivity::LOCKING_MAP)) {
                abort(400, '锁定/解锁标识提交错误');
            }

            $activity->update(['locking' => (int) $request->get('locking')]);
        } catch (ModelNotFoundException $e) {
            $this->response->errorBadRequest('未找到该活动');
        }

        return $this->response->created(null);
    }

    public function info($id)
    {
        return $this->response->array(
            CommodityActivity::query()
                ->with([
                    'commodity' => fn($builder) => $builder->with(['attributes', 'skus']),
                    'attachments',
                    'labels',
                    'attributes.values',
                    'skus' => fn($builder) => $builder->with(['values', 'attachments']),
                    'materials.attachments',
                    'moments.attachments',
                ])
                ->findOrFail($id)
                ->toArray()
        );
    }

    public function materialSetting(Request $request, $id)
    {
        try {
            DB::beginTransaction();
            $commodityActivities = CommodityActivity::query()->with('materials.attachments')->findOrFail($id);

            // 推送时间
            $pushDate = Carbon::parse($request->get('specify_the_push_date', Carbon::now()));
            $specifyThePushTime = $request->get('specify_the_push_time', Carbon::now()->addMinute()->format('H:i') . ':00');

            $pushSetting = $request->get('push_setting');
            if ((int) $pushSetting === CommodityActivity::PUSH_SETTING_REGULARLY) {
                $specifyThePushTime = Carbon::parse($pushDate)->format('H:i');
            } elseif ((int) $pushSetting === CommodityActivity::PUSH_SETTING_NOW) {
                $pushDate = Carbon::now();
                // 立即推送的情况 一分钟后
                $specifyThePushTime = Carbon::now()->addMinute()->format('H:i') . ':00';
            }
            $commodityActivities->update([
                'push_setting' => $pushSetting,
                'specify_the_push_date' => $pushDate->subSeconds($pushDate->second)->format('Y-m-d H:i:s'),
                'specify_the_push_time' => $specifyThePushTime
            ]);
            // 素材
            foreach ($commodityActivities->materials as $item) {
                if ($item['type'] === CommodityActivityPushMaterial::TYPE_PICTURE) {
                    // 删掉对应附件关联
                    $item->attachments()->detach();
                }
            }

            $commodityActivities->materials()->delete(); // 先删除
            // 再重新添加, 2020-10-13 16:27:22 现在是按照 id desc,这前端提交的顺序需要翻转下,才不会"倒过来"
            foreach (array_reverse($request->get('material')) as $material) {
                $model = $commodityActivities->materials()->create([
                    'type' => $material['type'],
                    'content' => (int) $material['type'] !== CommodityActivityPushMaterial::TYPE_PICTURE ? $material['value'] : ''
                ]);
                if ($material['type'] === CommodityActivityPushMaterial::TYPE_PICTURE) {
                    $model->attachments()->sync($material['value']);
                }
            }

            DB::commit();
        } catch (\Exception $e) {
            DB::rollBack();

            $this->response->errorBadRequest($e->getMessage());
        }

        \App\Services\Commodity::forgetCommodityActivityInfoCache($id); // 清理缓存

        activity()
            ->on($commodityActivities)
            ->by(\Auth::user())
            ->withProperty('ip', app('request')->ip())
            ->log(sprintf('修改商品活动(%s)素材', $commodityActivities['numbering'] ?? ''));
        return $this->response->noContent();
    }

    public function momentsSetting(Request $request, $id)
    {
        try {
            DB::beginTransaction();
            $commodityActivities = CommodityActivity::query()->with('moments.attachments')->findOrFail($id);

            // 素材
            foreach ($commodityActivities->moments as $item) {
                // 删掉对应附件关联
                $item->attachments()->detach();
            }

            // 先删除
            $commodityActivities->moments()->delete();
            // 再重新添加
            foreach (array_reverse($request->get('moments')) as $moment) {
                $model = $commodityActivities->moments()->create([
                    'content' => $moment['content']
                ]);
                $model->attachments()->sync($moment['material']);
            }

            DB::commit();
        } catch (\Exception $e) {
            DB::rollBack();

            $this->response->errorBadRequest($e->getMessage());
        }

        \App\Services\Commodity::forgetCommodityActivityInfoCache($id); // 清理缓存

        return $this->response->noContent();
    }

    public function stockingExport($id)
    {
        $commodityActivities = CommodityActivity::query()->with([
            'commodity' => function ($builder) {
                $builder
                    ->withTrashed()
                    ->with(['supplier']);
            }, 'attributes', 'skus.values'
        ])->findOrFail($id);

        // 备货单 活动编号 活动名称 供应商名称
        preg_match_all('/[\x{4e00}-\x{9fa5}a-zA-Z0-9]/u', $commodityActivities['commodity']['supplier']['name'] ?? '', $supplierName); // 过滤特殊字符
        return (new StockingExport($commodityActivities))
            ->download(sprintf(
                '备货单_%s_%s_%s.xls',
                $commodityActivities['numbering'],
                implode('', $supplierName[0]),
                Carbon::now()->format('YmdHisu')
            ));
    }

    public function invoiceExport($id)
    {
        $commodityActivities = CommodityActivity::query()->with(['commodity.supplier', 'attributes'])->findOrFail($id);

        // 去掉特殊字符
        $supplierName = $commodityActivities['commodity']['supplier']['name'] ?? '';
        $supplierName = str_replace(['\\', '/'], '', $supplierName);
        $commodityName = $commodityActivities['commodity']['name'] ?? '';
        $commodityName = str_replace(['\\', '/'], '', $commodityName);

        return (new InvoiceExport($commodityActivities))->download(
            sprintf(
                '%s_%d_%s.xls',
//                Carbon::parse($commodityActivities['panic_buying_start_time'])->format('YmdHis'), // 开团时间
                $commodityName, // 活动(商品)名称
                $commodityActivities['id'], // 活动编号
                $supplierName, // 供应商名称
            ));
    }

    /**
     * 编辑 Sku
     * @param Request $request
     * @param $id
     */
    public function updateSku(Request $request, $id)
    {
        $reqData = validator(array_merge($request->all(), ['id' => $id]), [
            'id' => 'required|exists:commodity_activity_sku',
            'stock' => 'integer|min:0'
        ], [
            'id.required' => 'SKU 不存在',
            'id.exists' => 'SKU 不存在',
            'stock.integer' => '库存数不合法',
            'stock.min' => '库存数不得小于零',
        ])->validate();

        CommodityActivitySku::query()->findOrFail($id)->update(['stock' => $request['stock']]);

        return $this->response->noContent();
    }
}
